#pragma once

#include "QueryPreProcessor.h"
#include "DAGNode.h"

class myexceptionQ: public exception {
  virtual const char* what() const throw() {
    return "Syntax error in PQL";
  }
} myexQ;

class undeclaredSynonymException: public exception {
  virtual const char* what() const throw() {
    return "Use of undeclared synonym in PQL";
  }
} undeclaredSynonymException;

class invalidAttributeType: public exception {
  virtual const char* what() const throw() {
    return "Invalid attribute type";
  }
} invalidAttributeType;

class invalidSynonymInWith: public exception {
  virtual const char* what() const throw() {
    return "Invalid synonym use in with clause";
  }
} invalidSynonymInWith;

class withLHSRHSMisMatch: public exception {
  virtual const char* what() const throw() {
    return "LHS and RHS of with clause mismatch";
  }
} withLHSRHSMisMatch;

class invalidDesignEntityException: public exception {
  virtual const char* what() const throw() {
    return "Declaration of invalid design entity in PQL";
  }
} invalidDesignEntityException;

class invalidIDENTException: public exception {
  virtual const char* what() const throw() {
    return "Invalid IDENT in PQL";
  }
} invalidIDENTException;

class invalidNOMBREException: public exception {
  virtual const char* what() const throw() {
    return "Invalid NAME in PQL";
  }
} invalidNOMBREException;

class invalidPatternDesignEntityException: public exception {
  virtual const char* what() const throw() {
    return "Use of invalid design entity in pattern clause in PQL";
  }
} invalidPatternDesignEntityException;

class invalidLHSPatternException: public exception {
  virtual const char* what() const throw() {
    return "Invalid Left Hand Side in pattern clause in PQL";
  }
} invalidLHSPatternException;

class LHSPatternNotVariableException: public exception {
  virtual const char* what() const throw() {
    return "Left Hand Side of pattern clause in PQL ,if a synonym, cannot be anything other than a variable";
  }
} LHSPatternNotVariableException;

QueryPreProcessor::QueryPreProcessor(PKB* pkb) {
	myPKB = pkb;
};

vector<vector<string>> QueryPreProcessor::processQuery(string query) {
	istringstream iss(query);
	string s;
	
	
	tokens.clear(); //clear the token vector for a fresh start
	parsed.clear();
	finalPackage.clear();
	withPackage.clear();
	result.clear();
	isCount = "NULL";
	isCallProcName = "FALSE";
	globalClauseCounter = 0;
	while(iss >> s) {
		tokens.push_back(s);
	}

	tokens = SpaceAdjuster(tokens, ';'); // initialising of synonym
	tokens = SpaceAdjusterV3(tokens, ';');
	tokens = SpaceAdjuster(tokens, '(');
	tokens = SpaceAdjuster(tokens, ',');
	tokens = SpaceAdjuster(tokens, ')');

	tokens = SAremoveSpaceFront(tokens, '(');

	tokens = SpaceAdjusterV3(tokens, ',');
	tokens = SAremoveSpaceFront(tokens, '"');
	tokens = SAremoveSpaceRear(tokens, '"');
	
	
	try	{
		int i = 0;
		if(tokens.at(0)!="ditto"||tokens.at(0)!="Select")
		{
		synTable.clear();
		commonsTable.clear();
		myPKB->result->bags.clear();
		//assuming Select keyword will be found, during iteration 3, we need to handle the case where there is no Select keyword using a timeout
		while (tokens.at(i) != "Select") { 
				//for initialization of synonyms
				if (!((tokens.at(i) == "stmt")
					|| (tokens.at(i) == "assign")
					|| (tokens.at(i) == "while")
					|| (tokens.at(i) == "if")
					|| (tokens.at(i) == "call")
					|| (tokens.at(i) == "variable")
					|| (tokens.at(i) == "constant")
					|| (tokens.at(i) == "procedure")
					|| (tokens.at(i) == "prog_line"))) {
					fail = false;
					throw invalidDesignEntityException;
				}
				NAME nama = tokens.at(i);
				while(tokens.at(i+1)[tokens.at(i+1).size()-1]!=';' )
				{
					tokens.at(i+1).erase(tokens.at(i+1).size()-1, 1);//delete the comma or semicolon. Eg assign a;
				
					validateNAME(tokens.at(i+1));//check syntax grammar 
					synTable.insert(std::make_pair<NAME, DESIGN_ENTITY>(tokens.at(i+1), nama)); 
					set<INDEX> newSet;
					commonsTable.insert(std::make_pair<NAME, set<INDEX>>(tokens.at(i+1), newSet)); 
					hash_set<INDEX> newSet2;
					myPKB->result->bags.insert(std::make_pair<NAME, hash_set<INDEX>>(tokens.at(i+1), newSet2)); 
					++i;
				}
				tokens.at(i+1).erase(tokens.at(i+1).size()-1, 1);//delete the comma or semicolon. Eg assign a;
				validateNAME(tokens.at(i+1));//check syntax grammar 
				synTable.insert(std::make_pair<NAME, DESIGN_ENTITY>(tokens.at(i+1), nama)); 
				set<INDEX> newSet;
				commonsTable.insert(std::make_pair<NAME, set<INDEX>>(tokens.at(i+1), newSet)); 
				hash_set<INDEX> newSet2;
				myPKB->result->bags.insert(std::make_pair<NAME, hash_set<INDEX>>(tokens.at(i+1), newSet2)); 
				i += 2;
		}
		}
		else
		{ 
			if(tokens.at(0)=="Select")
			{
				synTable.clear();
			}
			i = 1;
		}
		string tempStr = tokens.at(i+1);
		size_t found;

		found = tempStr.find(".");
		string MAINfirst, MAINsecond, MAINType;

		if (found!=std::string::npos) 
		{
			MAINfirst = tempStr.substr(0,found);
			MAINsecond = tempStr.substr(found+1,tempStr.length()-1);
		
			unordered_map<NAME,NAME>::const_iterator got = synTable.find(MAINfirst);
			if (got == synTable.end()) {
				throw undeclaredSynonymException;
			} else {
				MAINType = synTable.at(MAINfirst);
			}
		
			if(MAINType=="procedure")
			{
				if(!(MAINsecond=="procName"))
				{
					throw invalidAttributeType;
				}
			}
			else if(MAINType=="call")
			{
				if((!(MAINsecond=="procName"))&&(!(MAINsecond=="stmt#")))
				{
					throw invalidAttributeType;
				}
				if(MAINsecond=="procName")
				{
					isCallProcName = "TRUE";
				}
			}
			else if(MAINType=="variable")
			{
				if(!(MAINsecond=="varName"))
				{
					throw invalidAttributeType;
				}
			}
			else if(MAINType=="constant")
			{
				if(!(MAINsecond=="value"))
				{
					throw invalidAttributeType;
				}
			}
			else if(MAINType=="stmt")
			{
				if(!(MAINsecond=="stmt#"))
				{
					throw invalidAttributeType;
				}
			}
			else
			{
				throw invalidAttributeType;
			}
			tokens.at(i+1) = MAINfirst;
		}
		vector<string> lolo;
			lolo.clear();
		if(tokens.at(i+1)=="BOOLEAN")
		{
			operand = "BOOLEAN"; 
			operandType = "BOOLEAN";
		}
		else
		{
			string localString = tokens.at(i+1);
			string preOperand = tokens.at(i+1);

			if(localString.size()>7)
			{
				preOperand = localString.substr(6, localString.size()-7);
				isCount = "COUNT";
			}

			
			if(preOperand.front()=='<')
			{
				string tempTuple = "";
				while (tokens.at(i+1).back()!='>')
				{
					tempTuple.append(tokens.at(i+1));
					++i;
				}
				tempTuple.append(tokens.at(i+1));
				++i;
				tempTuple = tempTuple.substr(0,tempTuple.size()-1);
				tempTuple = tempTuple.substr(1,tempTuple.size()-1);
				int z = 0;
				string tempTupleSub;
				while(z<tempTuple.length())
				{
					tempTupleSub.clear();
					while(z<tempTuple.length()&&tempTuple.at(z)!=',')
					{
						char Result[16];
						sprintf_s(Result, "%c", tempTuple.at(z));
						tempTupleSub.append(Result);
						++z;
					}
					++z;
					lolo.push_back(tempTupleSub);
				}
				operandType = "tuple";
				char Result[16];
				sprintf_s(Result, "%d", lolo.size());
				operand = Result;
				--i;
			}
			else
			{
				unordered_map<NAME, NAME>::const_iterator got = synTable.find(preOperand);
				if (got == synTable.end()) {
					throw undeclaredSynonymException;
				} else {
					operand = got->first; 
					operandType = got->second;
				}
			}
		}
		selectR.clear();
		
		selectR.push_back(operand);
		selectR.push_back(operandType);
		selectR.push_back(isCount);
		selectR.push_back(isCallProcName);

		for(int j = 0; j < lolo.size(); j++)
		{
			string tupOperand;
			string tupOpertype;
			unordered_map<NAME, NAME>::const_iterator got = synTable.find(lolo.at(j));
			
			if (got == synTable.end()) {
				throw undeclaredSynonymException;
			} else {
				tupOperand = got->first; 
				tupOpertype = got->second;
			}
			selectR.push_back(tupOperand);
			selectR.push_back(tupOpertype);
		}

		parsed.push_back(selectR);	
		int y = i+2;

		//check the rest of the tokens after "select x"
		string lastClause; 
		
		while (((signed)tokens.size()-1) > y) 
		{
			int b = y;
			match(tokens.at(y),"with","pattern","such","and");
		
			if (tokens.at(b) == "such") 
			{
				++y;
				while ((y<(signed)tokens.size())&&(tokens.at(y) != "such"&&tokens.at(y) != "with"&&tokens.at(y) != "pattern"&&tokens.at(y) != "and")) 
				{
					++y;
				}
				lastClause = "such";
				Such(b,y-1);
				parsed.push_back(result);
			}
			else if (tokens.at(b) == "pattern") 
			{
				++y;
				while ((y<(signed)tokens.size())&&(tokens.at(y) != "such"&&tokens.at(y) != "with"&&tokens.at(y) != "pattern"&&tokens.at(y) != "and")) 
				{
					++y;
				}
				lastClause = "pattern";
				Pattern(b,y-1);
				parsed.push_back(finalPackage);
			}
			else if (tokens.at(b) == "with") 
			{
				++y;
				while ((y<(signed)tokens.size())&&(tokens.at(y) != "such"&&tokens.at(y) != "with"&&tokens.at(y) != "pattern"&&tokens.at(y) != "and")) 
				{
					++y;
				}
				lastClause = "with";
				string tp;
				tp.clear();
				for(int i = b;i<y;i++)
				{
					tp.append(tokens.at(i)+" ");
				}
				With(tp);
				parsed.push_back(withPackage);
			}
			else//and
			{
				++y;
				int inni = y;
				if((tokens.at(y) == "pattern"))
				{
					++y;
					while ((y<(signed)tokens.size())&&(tokens.at(y) != "such"&&tokens.at(y) != "with"&&tokens.at(y) != "pattern"&&tokens.at(y) != "and")) 
					{
						++y;
					}

					Pattern(inni,y-1);
					parsed.push_back(finalPackage);
				}
				else
				{
					while ((y<(signed)tokens.size())&&(tokens.at(y) != "such"&&tokens.at(y) != "with"&&tokens.at(y) != "pattern"&&tokens.at(y) != "and")) 
					{
						++y;
					}
					if(lastClause == "such")
					{
						Such(inni-2,y-1);
						parsed.push_back(result);
					}
					else if(lastClause == "pattern")
					{
						//Pattern(inni,y-1);
						//parsed.push_back(finalPackage);
					}
					else
					{
						string tp;
						tp.clear();
						for(int i = inni-1;i<y;i++)
						{
							tp.append(tokens.at(i)+" ");
						}
						With(tp);
						parsed.push_back(withPackage);
					}
				}
			}
		}
	

	} catch (exception& e) {
		cout << e.what() << '\n';
	}

	vector<int> clauseOrder;
	vector<int> bitClause;
	for(int i = 0;i<globalClauseCounter; i++)
	{
		bitClause.push_back(1);
	}
	for (auto it = commonsTable.begin(); it != commonsTable.end(); ++it)
	{
		if(it->second.size()>1)
		{
			for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2)
			{
				if(bitClause.at(*it2-1))
				{
					clauseOrder.push_back(*it2);
				}
				bitClause.at(*it2-1) = 0;
			}
		}
	}
	for(int i = 0;i<bitClause.size(); i++)
	{
		if(bitClause.at(i))
		{
			clauseOrder.push_back(i+1);
		}
	}
	vector<string> clauseStringOrder;
	for(int i = 0;i<clauseOrder.size(); i++)
	{
		char Result[16];
		sprintf_s(Result, "%d", clauseOrder.at(i));
		clauseStringOrder.push_back(Result);
	}
	parsed.push_back(clauseStringOrder);
	//---------------------------------NEW------------------------------------

	vector<int> bitClauseNew;
	for(int i = 0;i<globalClauseCounter; i++)
	{
		bitClauseNew.push_back(1);
	}

	DAGNode root(operand);
	//=====================================================================
	cout << "QPP OK" << endl;
	return parsed;
};

vector<string> QueryPreProcessor::SpaceAdjuster(vector<string> tokens, char param) {
	vector<string> novel;	
	string firstHalf;
	string secondHalf;
	string temp;
	string vada;
	
	for (int i = 0; i < (signed)tokens.size(); i++) {
		if (tokens.at(i).front() == param) {
			vada = novel.at(novel.size() - 1);
			novel.pop_back();
			novel.push_back(vada + "" + tokens.at(i));
		} else {
			novel.push_back(tokens.at(i));
		}
	}
	return novel;
};

vector<string> QueryPreProcessor::SAremoveSpaceFront(vector<string> tokens, char param) {
	vector<string> novel;
	string firstHalf;
	string secondHalf;
	string temp;
	string vada;

	for (int i = 0; i < (signed)tokens.size(); i++)	{
		if (tokens.at(i)[tokens.at(i).length()-1] == param && i!=tokens.size()-1 && tokens.at(i+1)!="=" && tokens.at(i+1)!="such" && tokens.at(i+1)!="pattern" && tokens.at(i+1)!="with" && tokens.at(i+1)!="and") {
			novel.push_back(tokens.at(i) + "" + tokens.at(i + 1));
			++i;
		} else {
			novel.push_back(tokens.at(i));
		}
	}
	return novel;
};

vector<string> QueryPreProcessor::SpaceAdjusterV3(vector<string> tokens, char param) {
	vector<string> novel;
	size_t found;
	string firstHalf;
	string secondHalf;
	string temp;
	string vada;
	
	for (int i = 0; i < (signed)tokens.size(); i++)	{
		temp = tokens.at(i);
		found = temp.find(param);
		if (found != std::string::npos&&(temp[temp.length() - 1] != param)) {
			firstHalf = temp.substr(0, found);
			secondHalf = temp.substr(found + 1, temp.length() - 1);
			if (firstHalf != "") {
				novel.push_back(firstHalf + "" + param);
			}
			if (secondHalf != "") {
				novel.push_back(secondHalf);
			}
		} else {
			novel.push_back(tokens.at(i));
		}
	}
	return novel;
};

vector<string> QueryPreProcessor::SAremoveSpaceRear(vector<string> tokens, char param) {
	vector<string> novel;
	string firstHalf;
	string secondHalf;
	string temp;
	string vada;
	
	for (int i = 0; i < (signed)tokens.size(); i++) {
		if (tokens.at(i).front() == param && ((tokens.at(i - 1) != "with") && (tokens.at(i - 1)[tokens.at(i - 1).length() - 1] != '=') && (tokens.at(i - 1)[tokens.at(i - 1).length() - 1] != ',') && (tokens.at(i - 1)[tokens.at(i - 1).length() - 1] != '_'))) {
			vada = novel.at(novel.size() - 1);
			novel.pop_back();
			novel.push_back(vada + "" + tokens.at(i));
		} else {
			novel.push_back(tokens.at(i));
		}
	}
	return novel;
};

void QueryPreProcessor::Pattern(int i, int end) {
	++globalClauseCounter;

	finalPackage.clear();

	string tempCopy = tokens.at(i + 1);

	size_t foundOpenBracket = tempCopy.find("(");
	string patternOpe;
	if (foundOpenBracket!=std::string::npos)
	{
		patternOpe = tokens.at(i + 1).substr(0, foundOpenBracket);
	}
	else
	{
		throw myexQ;
	}
	string tempCopy2 = tempCopy;

	unordered_map<NAME, NAME>::const_iterator got = synTable.find(patternOpe);
	string patternOpeType;
	if (got == synTable.end()) {
		throw undeclaredSynonymException;
	} else {
		patternOpeType = synTable.at(patternOpe);
		commonsTable.at(patternOpe).insert(globalClauseCounter);
		if (!(patternOpeType == "assign"||patternOpeType == "while"||patternOpeType == "if") ) {
			throw invalidPatternDesignEntityException;
		}
	}
	
	finalPackage.push_back("pattern");
	finalPackage.push_back(patternOpe);
	finalPackage.push_back(patternOpeType);
	string beforeRemoveComma = tempCopy2.substr(foundOpenBracket+1, tempCopy2.length());
	string afterRemoveComma = beforeRemoveComma.substr(0,beforeRemoveComma.length()-1);

	string LHSType = getParamTypeEntityRef(afterRemoveComma);
	
	string LHS;
	if (LHSType == "invalid") {
		throw invalidLHSPatternException;
	} else if (LHSType == "ident") {
		afterRemoveComma = afterRemoveComma.substr(0,afterRemoveComma.length()-1);
		afterRemoveComma = afterRemoveComma.substr(1,afterRemoveComma.length());
		LHS = afterRemoveComma;
	} else if (LHSType == "syn") {
	
		LHS = afterRemoveComma;
		
		unordered_map<NAME, NAME>::const_iterator got = synTable.find(LHS);	
		if (got == synTable.end()) {
			throw undeclaredSynonymException;
		} else {
			commonsTable.at(LHS).insert(globalClauseCounter);

			if (synTable.at(LHS) != "variable") {
				throw LHSPatternNotVariableException;
			}
		}
	}
	
	finalPackage.push_back(LHS);
	finalPackage.push_back(LHSType);

	string joinStr;
	for (int x = i + 2; x < end + 1; x++) {
		joinStr.append(tokens.at(x));
	}

	if(patternOpeType=="assign") 
	{

		string::iterator it = joinStr.end(); 
		--it;
	
		if (*it != ')') {

			throw myexQ;
		}
		it = joinStr.begin();
		string isSub;
		if (*it == '_') {
			if(joinStr.length()>2)
			{
				string::iterator it2 = joinStr.end();
				--it2;
				--it2;
				if (*it2 != '_') {

					throw myexQ;
				}
				isSub = "TRUE";
			}
		} else {
			string::iterator it2 = joinStr.end();
			--it2;
			--it2;
			if (*it2 == '_') {

				throw myexQ;
			}

			isSub = "FALSE";
		}

		string RHS;

		if (joinStr != "_)") 
		{
			if(isSub=="TRUE")
			{
				RHS = joinStr.substr(1, joinStr.length() - 3);
			}
			else
			{
				RHS = joinStr.substr(0, joinStr.length() - 1);;
			}
		} 
		else 
		{
			RHS = "_";
		}

		string RHStype;
		if (RHS == "_") 
		{
			RHStype = "underscore";
			finalPackage.push_back(RHS);
			finalPackage.push_back("underscore");
			finalPackage.push_back(isSub);
		} 
		else 
		{
			if (RHS[0] != '"') 
			{

				throw myexQ;
			}
			if (RHS[RHS.length() - 1] != '"') 
			{

				throw myexQ;
			}
			
			RHS = RHS.substr(1, RHS.length());
			RHS = RHS.substr(0, RHS.length() - 1);
			
			vector<string> preRHS;
			string gag="";
			preRHS.push_back("|");
			for(int i = 0;i<RHS.length();i++)
			{
				if(RHS.at(i)=='+'||RHS.at(i)=='-'||RHS.at(i)=='*'||RHS.at(i)=='('||RHS.at(i)==')')
				{
					gag.clear();
					gag.append(1,RHS.at(i));
					preRHS.push_back(gag);
					preRHS.push_back("|");
					gag.clear();
				}
				else{
				do
				{
					gag.append(1,RHS.at(i));
					++i;
				}while((i<RHS.length())&&(RHS.at(i)!='+'&&RHS.at(i)!='-'&&RHS.at(i)!='*'&&RHS.at(i)!='('&&RHS.at(i)!=')'));
				
				preRHS.push_back(gag);
				preRHS.push_back("|");
				gag.clear();
				--i;
				}
			}
			string res = myPKB->stmtTable->buildPatternB(preRHS);
			
			finalPackage.push_back(res);
			finalPackage.push_back("multiple");
			finalPackage.push_back(isSub);
		}
	}

	else if(patternOpeType=="while") 
	{
		if(joinStr!="_)")
		{
			throw myexQ;
		}
		else
		{
			finalPackage.push_back("dummy");
			finalPackage.push_back("dummy");
			finalPackage.push_back("dummy");

		}
	}
	else
	{
		if(joinStr!="_,_)")
		{
			throw myexQ;
		}
		else
		{
			finalPackage.push_back("dummy");
			finalPackage.push_back("dummy");
			finalPackage.push_back("dummy");
		}
	}

};

//expected string : with x = x
void QueryPreProcessor::With(string toProcess) 
{
	++globalClauseCounter;
	withPackage.clear();
	vector<string> tokens;
	istringstream iss(toProcess);

	string tempStr;
	size_t found;

	string LHSfirst;
	string LHSsecond;
	string LHSType;
	string LHSDataType;

	string RHSfirst;
	string RHSsecond;
	string RHSType;
	string RHSDataType;

	while(iss >> tempStr) {
		tokens.push_back(tempStr);
	}

	//match(tokens.at(0),"with");
	
	tempStr = tokens.at(1);
	if(tempStr.front()=='\"' && tempStr.back()=='\"')
	{
		LHSDataType="NAME";
		LHSType = "string";
		tempStr.erase(tempStr.size()-1, 1);
		tempStr.erase(0, 1);
		LHSfirst = tempStr;
	}
	else if(isInteger(tempStr))
	{
		LHSDataType="INTEGER";
		LHSType = "integer";
		LHSfirst = tempStr;
	}
	else
	{
		found = tempStr.find(".");
	
		if (found!=std::string::npos) 
		{
			LHSfirst = tempStr.substr(0,found);
			LHSsecond = tempStr.substr(found+1,tempStr.length()-1);

			unordered_map<NAME,NAME>::const_iterator got = synTable.find(LHSfirst);
			
			if (got == synTable.end()) {
				throw undeclaredSynonymException;
			} else {
				commonsTable.at(LHSfirst).insert(globalClauseCounter);
				LHSType = synTable.at(LHSfirst);
			}

			if(LHSType=="procedure")
			{
				if(!(LHSsecond=="procName"))
				{
					throw invalidAttributeType;
				}
				else
				{
					LHSDataType = "NAME";
				}
			}
			else if(LHSType=="call") 
			{
				if(!(LHSsecond=="procName"||LHSsecond=="stmt#"))
				{
					throw invalidAttributeType;
				}
				else
				{
					LHSDataType = "NAME";
				}
			}
			else if(LHSType=="variable")
			{
				if(!(LHSsecond=="varName"))
				{
					throw invalidAttributeType;
				}
				else
				{
					LHSDataType = "NAME";
				}
			}
			else if(LHSType=="constant")
			{
				if(!(LHSsecond=="value"))
				{
					throw invalidAttributeType;
				}
				else
				{
					LHSDataType = "INTEGER";
				}
			}
			else if(LHSType=="stmt"|LHSType=="call"|LHSType=="assign"|LHSType=="while"|LHSType=="if")
			{
				if(LHSType=="call")
				{
					if((LHSsecond=="stmt#"))
					{
						LHSDataType = "INTEGER";
					}
					else if((LHSsecond=="procName"))
					{
						LHSDataType = "NAME";
					}
					else
					{
						throw invalidAttributeType;
					}
				}
				else
				{
					if(!(LHSsecond=="stmt#"))
					{
						throw invalidAttributeType;
					}
					else
					{
						LHSDataType = "INTEGER";
					}
				}
			}
		}
		else
		{
			unordered_map<NAME,NAME>::const_iterator got = synTable.find(tempStr);
			if (got == synTable.end()) {
				throw undeclaredSynonymException;
			} else {
				commonsTable.at(tempStr).insert(globalClauseCounter);
				LHSType = synTable.at(tempStr);
				if(LHSType!="prog_line")
				{
					throw invalidSynonymInWith;
				}
				else
				{
					LHSfirst = tempStr;
					LHSDataType = "INTEGER";
				}
			}
		}
	}
	//.......................................................................
	match(tokens.at(2),"=");
	//.......................................................................
	tempStr = tokens.at(3);
	found = tempStr.find(".");
	if(tempStr.front()=='\"' && tempStr.back()=='\"')
	{
		RHSDataType="NAME";
		RHSType = "string";
		tempStr.erase(tempStr.size()-1, 1);
		tempStr.erase(0, 1);
		RHSfirst = tempStr;
	}
	else if(isInteger(tempStr))
	{
		RHSDataType="INTEGER";
		RHSType = "integer";
		RHSfirst = tempStr;
	}
	else 
	{
		if (found!=std::string::npos) 
		{
			RHSfirst = tempStr.substr(0,found);
			RHSsecond = tempStr.substr(found+1,tempStr.length()-1);
		
			unordered_map<NAME,NAME>::const_iterator got = synTable.find(RHSfirst);
			if (got == synTable.end()) {
				throw undeclaredSynonymException;
			} else {
				commonsTable.at(RHSfirst).insert(globalClauseCounter);
				RHSType = synTable.at(RHSfirst);
			}
		
			if(RHSType=="procedure")
			{
				if(!(RHSsecond=="procName"))
				{
					throw invalidAttributeType;
				}
				else
				{
					RHSDataType = "NAME";
				}
			}
			else if(RHSType=="call")
			{
				if(RHSsecond=="procName")
				{
					RHSDataType = "NAME";
				}
				else if(RHSsecond=="stmt#")
				{
					RHSDataType = "INTEGER";
				}
				else
				{
					throw invalidAttributeType;
				}
			}
			else if(RHSType=="variable")
			{
				if(!(RHSsecond=="varName"))
				{
					throw invalidAttributeType;
				}
				else
				{
					RHSDataType = "NAME";
				}
			}
			else if(RHSType=="constant")
			{
				if(!(RHSsecond=="value"))
				{
					throw invalidAttributeType;
				}
				else
				{
					RHSDataType = "INTEGER";
				}
			}
			else if(RHSType=="stmt"|RHSType=="call"|RHSType=="assign"|RHSType=="while"|RHSType=="if")
			{
				if(RHSType=="call")
				{
					if((RHSsecond=="stmt#"))
					{
						RHSDataType = "INTEGER";
					}
					else if((RHSsecond=="procName"))
					{
						RHSDataType = "NAME";
					}
					else
					{
						throw invalidAttributeType;
					}
				}
				else
				{
					if(!(RHSsecond=="stmt#"))
					{
						throw invalidAttributeType;
					}
					else
					{
						RHSDataType = "INTEGER";
					}
				}
			}
		}
		else
		{
			unordered_map<NAME,NAME>::const_iterator got = synTable.find(tempStr);
			if (got == synTable.end()) {
				throw undeclaredSynonymException;
			} else {
				commonsTable.at(tempStr).insert(globalClauseCounter);
				RHSType = synTable.at(tempStr);
				if(RHSType!="prog_line")
				{
					throw invalidSynonymInWith;
				}
				else
				{
					RHSfirst = tempStr;
					RHSDataType = "INTEGER";
				}
			}
		}
	}

	if(LHSDataType!=RHSDataType)
	{
		throw withLHSRHSMisMatch;
	}

	withPackage.push_back("with");

	if(RHSType=="prog_line")
	{
		RHSType = "stmt";
	}
	if(LHSType =="prog_line")
	{
		LHSType = "stmt";
	}
	withPackage.push_back(LHSfirst);
	withPackage.push_back(LHSType);
	withPackage.push_back(RHSfirst);
	withPackage.push_back(RHSType);

};

void QueryPreProcessor::Such(int i, int end) {
	++globalClauseCounter;
	//match(tokens.at(i+1), "that");
	size_t foundBracket = tokens.at(i + 2).find_first_of("(");
	//if the position of bracket is more than the length of the token. Eg. Parent( ,5)
	if ((int)foundBracket > (int)(tokens.at(i + 2).length())) {
		throw myexQ;
	}
	string tCopy = tokens.at(i + 2);
	relation = tokens.at(i + 2).substr(0, foundBracket); //get the relation by finding substring from 0th post to post of "("
	relationfirstParam = tCopy.substr(foundBracket + 1); //get the first param after the "("
	relationfirstParam.erase(relationfirstParam.size() - 1, 1); //remove the comma
	relationsecondParam = tokens.at(i + 3).erase(tokens.at(i + 3).size() - 1, 1); //remove the ")" to get second param
	
	
	result.clear();
	string firstParamType = getParamType(relationfirstParam); 
	string secondParamType = getParamType(relationsecondParam);
	string firstParamSynType = "NULL";
	string secondParamSynType = "NULL";
	
	if (firstParamType == "syn") {
		commonsTable.at(relationfirstParam).insert(globalClauseCounter);
		firstParamSynType=synTable.at(relationfirstParam);
	}

	if (secondParamType == "syn") {
		commonsTable.at(relationsecondParam).insert(globalClauseCounter);
		secondParamSynType = synTable.at(relationsecondParam);
	}

	if (firstParamType == "ident") {
		relationfirstParam=relationfirstParam.substr(1, relationfirstParam.size() - 2);
		validateIDENT(relationfirstParam);
	}

	if (secondParamType == "ident") {
		relationsecondParam=relationsecondParam.substr(1, relationsecondParam.size() - 2);
		validateIDENT(relationsecondParam);
	}

	if (relation == "Next" || relation == "Follows" || relation == "Follows*" || relation == "Parent" || relation == "Parent*") {
		if (!(firstParamSynType == "assign" || firstParamSynType == "while" || firstParamSynType == "if" || firstParamSynType == "NULL" || firstParamSynType == "stmt" || firstParamSynType == "prog_line")) {
			throw myexQ;
		}
	}

	if (relation == "Calls" || relation == "Calls*") {
		if (!(firstParamSynType == "procedure" || secondParamSynType == "procedure" || firstParamSynType == "NULL" || secondParamSynType == "NULL")) {
			throw myexQ;
		}
	}

	if(firstParamSynType=="prog_line")
	{
		firstParamSynType="stmt";
	}
	if(secondParamSynType=="prog_line")
	{
		secondParamSynType="stmt";
	}
	result.push_back("such that");
	result.push_back(relation);
	result.push_back(relationfirstParam);
	result.push_back(relationsecondParam);
	result.push_back(firstParamType);
	result.push_back(secondParamType);
	result.push_back(firstParamSynType);
	result.push_back(secondParamSynType);
};

bool QueryPreProcessor::match(string str, string ref) {
	if (str == ref) {
		return true;
	} else {
		throw myexQ;
	}
};

bool QueryPreProcessor::match(string str, string ref, string ref2, string ref3, string ref4) {
	if (str == ref || str == ref2 || str == ref3 || str == ref4) {
		return true;
	} else {
		throw myexQ;
	}
};

int QueryPreProcessor::numDigits(int x) {
	int i = 0;
	while (x > 0) {
		x /= 10;
		++i;
	}
	return i;
};

string QueryPreProcessor::getParamType(string param) {
	if (param == "_") {
		return "underscore";
	} else if(isdigit(param[0])) {
		for (int i = 0; i < (signed)param.length(); i++) {
			if (!isdigit(param[i])) {
				return "invalid";
			}
		}
		return "integer";
	} else if ((param[0] == '"') && (param[param.length() - 1] == '"')) {
		return "ident";
	} else {
		unordered_map<NAME,NAME>::const_iterator got = synTable.find(param);
		if (got == synTable.end()) {
			throw undeclaredSynonymException;
		} else {
			return "syn";
		}
	}
};

string QueryPreProcessor::getParamTypeEntityRef(string param) {
	if (param == "_") {
		return "underscore";
	} else if ((param[0] == '"') && (param[param.length() - 1] == '"')) {
		return "ident";
	} else {
		unordered_map<NAME,NAME>::const_iterator got = synTable.find(param);
		if (got == synTable.end()) {
			throw undeclaredSynonymException;
		} else {
			return "syn";
		}
	}
};

void QueryPreProcessor::validateNAMEINTEGER(string str) {
	if (!isalpha(str[0])) {
		stoi(str);
	}
	for(int i = 0; i < (signed)str.length(); i++) {
		if (!isalnum(str[i])) {
			throw myexQ;
		}
	}
};

bool QueryPreProcessor::hasEndsWith(string str, char ref) {
	if (str.back() == ref) {
		return true;
	} else {
		return false;
	}
};

bool QueryPreProcessor::checkNAME(string str) { 
	if (!isalpha(str[0])) {
		return false;
	}
	for (int i = 0; i < (signed)str.length(); i++) {
		if (!isalnum(str[i])) {
			return false;
		}
	}
};

void QueryPreProcessor::validateIDENT(string str) { 
	if (!isalpha(str[0])) {
		throw invalidIDENTException;
	}
	for (int i = 0; i < (signed)str.length(); i++) {
		if (!((isalnum(str[i])) || (str[i]=='#'))) {
			throw invalidIDENTException;
		}
	}
};

void QueryPreProcessor::validateNAME(string str) {
	if (!isalpha(str[0])) {
		throw invalidNOMBREException;
	}
	for (int i = 0; i < (signed)str.length(); i++) {
		if (!isalnum(str[i])) {
			throw invalidNOMBREException;
		}
	}
};

bool QueryPreProcessor::isInteger(string str) 
{
	try
	{
		stoi(str);
	}
	catch (exception& e) {
		return false;
	}
	return true;
};